/**
 * Team members:
 * HengXing Liu 2022229044
 * YiYang Zhang 2022229047
 * ZeJun Liu 2022229092
 */
package project;

import java.math.BigDecimal;
import java.util.*;
/**
 * A class to handle custom function management
 */
public class FunctionManage {
    ArrayList<String> fName;//all custom function name;
    Map<String, ArrayList<String>> fVariable;// (function, variables of function)
    Map<String, String> fExpression;// (function, expression of function)
    public FunctionManage(){
        fName = new ArrayList<>();
        fVariable = new HashMap<>();
        fExpression = new HashMap<>();
    }
    /**
     * check the custom function exists or not.
     */
    public boolean isContainFName(String functionName){
        return fName.contains(functionName);
    }
    /**
     * Gets the number of parameters for a specific function
     */
    public int getFArity(String functionName){
        return fVariable.get(functionName).size();
    }
    /**
     * add a function
     * if it exists ,then modify it.
     */
    public String addOrModify(String functionName, ArrayList<String> variables,Tokenizer tokenizer) throws LexicalErrorException, TokenException {
        if(!isContainFName(functionName))fName.add(functionName);
        fVariable.put(functionName, variables);
        return addExpression(functionName,tokenizer);
    }
    /**
     * add expression
     * only used in "addOrModify()".
     */
    private String addExpression(String functionName, Tokenizer tokenizer) throws LexicalErrorException, TokenException {
        String expression = "";
        while(tokenizer.hasNextToken()){
            String tmp = tokenizer.readNextToken().toString();
            if(tmp.equals("-U")){
                tmp = "-";
            }
            expression+=tmp;
        }
        fExpression.put(functionName, expression);
        return functionName;
    }
    /**
     * get all function name.
     */
    public ArrayList<String> getAllName() {
        return fName;
    }
    /**
     * get all (function, variables of function).
     */
    public Map<String, ArrayList<String>> getAllVariable() {
        return fVariable;
    }
    /**
     * get all (function, expression of function).
     */
    public Map<String, String> getAllExpression() {
        return fExpression;
    }
    /**
     * Gets the expression for the specified function.
     */
    public String getExpression(String functionName) {
        return fExpression.get(functionName);
    }

    /**
     * Deletes the specified custom function or all existing custom functions.
     */
    public void remove(List<String> functionNames, boolean flgF) throws Exception {
        if(!flgF)return;
        if(functionNames.isEmpty()){
            if(!fName.isEmpty()){
                for(int i = 0;i < fName.size();i++){
                    functionNames.add(fName.get(i));
                }
            }
        }

        for(String str : functionNames) {
            if(!isContainFName(str)){
                throw new VariableException(str + " is not a variable");
            }
        }

        for(String str : functionNames){
            fName.remove(str);
            String tmpVariables = fVariable.remove(str).toString();
            System.out.println( str + "(" +tmpVariables.substring(1,tmpVariables.length()-1)+") = "+ fExpression.remove(str)+" has been reset");
            if(LogManage.flag){
                LogManage.WriteFile(str + "(" +tmpVariables.substring(1,tmpVariables.length()-1)+") = "+ fExpression.remove(str)+" has been reset\n");
            }
        }
    }

    /**
     * Calculates the value of a function based on the function name and parameter values.
     */
    public Double evaluator(String funcionName, ArrayList<Double> opNum) throws LexicalErrorException, SyntaxErrorException, DivisionErrorException, VariableException, TokenException {
        ArrayList<String> variableName = fVariable.get(funcionName);
        String expression = getExpression(funcionName);
        Tokenizer nowTokenizer = new Tokenizer(expression);
        MathematicalEvaluator mathematicalEvaluator = new MathematicalEvaluator();
        mathematicalEvaluator.setTokenizer(nowTokenizer);
        //make Vm//这里应该搞一下last的问题
        VariableManage vm = new VariableManage();
        for(int i = 0;i < opNum.size();i++){
            vm.addOrModify(variableName.get(i), opNum.get(i));
        }
        //
        BigDecimal res = null;
        try {
            res = mathematicalEvaluator.Evaluator(vm, null, this);
        } catch (Exception e) {
            throw e;
        }
        return res.doubleValue();
    }
}
